package tenglang.zuul.zuul.filter;

import auth.server.impl.pojo.VistorRecordVo;
import auth.server.impl.service.VistorRecordService;
import auth.server.inter.AuthConstants;
import auth.server.inter.security.VistorRecord;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.*;
import java.nio.charset.Charset;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_FORWARD_FILTER_ORDER;

@Component
public class ResponseRecordFilter extends ZuulFilter {

    @Autowired
    private VistorRecordService vistorRecordService;

    /**
     * to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
     * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
     * We also support a "static" type for static responses see  StaticResponseFilter.
     * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
     *
     * @return A String representing that type
     */
    @Override
    public String filterType() {
        return POST_TYPE;
    }

    /**
     * filterOrder() must also be defined for a filter. Filters may have the same  filterOrder if precedence is not
     * important for a filter. filterOrders do not need to be sequential.
     *
     * @return the int order of a filter
     */
    @Override
    public int filterOrder() {
        return SEND_FORWARD_FILTER_ORDER;
    }

    /**
     * a "true" return from this method means that the run() method should be invoked
     *
     * @return true if the run() method should be invoked. false will not invoke the run() method
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter
     *
     * @return Some arbitrary artifact may be returned. Current implementation ignores it.
     * @throws ZuulException if an error occurs during execution.
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        if(requestContext.containsKey(AuthConstants.USER_ACCESS_LOGS) && (Boolean)requestContext.get(AuthConstants.USER_ACCESS_LOGS_ENABLE)){
            VistorRecord vistorRecord=(VistorRecord)requestContext.get(AuthConstants.USER_ACCESS_LOGS);
            VistorRecordVo vistorRecordVo=new VistorRecordVo(vistorRecord);
            InputStream stream = requestContext.getResponseDataStream();
            Throwable throwable = requestContext.getThrowable();
            int statusCode=requestContext.getResponseStatusCode();
            StringBuilder builder=new StringBuilder();
            try {
                String body = StreamUtils.copyToString(stream, Charset.forName("UTF-8"));
                builder.append(body);
                requestContext.setResponseDataStream(new ByteArrayInputStream(body.getBytes("UTF-8")));
                vistorRecordVo.setResponseSource(body);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(throwable!=null){
                vistorRecordVo.setErrorMessage(getStackTrace(throwable));
            }
            vistorRecordVo.setResponseCode(statusCode);
            vistorRecordService.saveOrUpdate(vistorRecordVo);
        }
        return null;
    }


    /**
     * 将异常堆栈转换为字符串
     * @param aThrowable 异常
     * @return String
     */
    public static String getStackTrace(Throwable aThrowable) {
        final Writer result = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(result);
        aThrowable.printStackTrace(printWriter);
        return result.toString();
    }

}
